home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-01
/
wmv12s.zip
/
FRONT.C
< prev
next >
Wrap
Text File
|
1993-01-04
|
14KB
|
447 lines
/* Program to move files and/or subdirectories - like mv on unix
** Written by Peter Wu July, 86 @ Faculty Support Center @ UW-Madison
** Compile with IBM C version 1.00 (or Microsoft C 3.00).
** Link with fstat, mv, normal, sector, absdr, func32h, break, putn /stack:5000
**
** This is module "front.c", the front end of mv. It looks at the sources
** and destination supplied by the user and determines whether to call
** mvdir to move/rename sub-directories or call rename to move/rename files.
*/
/* programmer's notes:
-- won't work on network disk
--
-- things to work on:
--
-- be interactive:
-- ask if user wants to replace existing file/directory
-- verbose mode where user must confirm each move
-- better error handling
-- make it work on network disks
-- detect and report write-protected disk if possible
-- allow /f option on destination?
--
-- bugs:
+--------------------------------+
| c: |
| subst p: /user/peter/mv/test |
| mv /usr/peter/mv/test . |
+--------------------------------+
-- The above sequence will prevent mv from removing the source directory after
-- the content is moved to the destination directory. I don't know any way to
-- detect this BEFORE moving the content. So my solution is to print a message
-- telling the user to remove the source directory (now empty) by hand after
-- he got rid of the 'subst drive'.
*/
#define LINT_ARGS
#define UNK_TYP 0
#define A_INT 0x80 /* interactive file attribute */
#define LASTDRV 26 /* drive# larger than this is network share disk */
#define NULL (char *) 0
#define PLEN 200 /* max. path length; hope it's long enough */
#include "dta.h"
#include "date.h"
#include <conio.h>
#include <string.h>
#include <stdlib.h>
char lastc(char *);
char getkey(char *);
int brk_st; /* original break status: 0=break off, 1=break on */
/* When break is on, user can break the program when any DOS function is
** called. When break is off, user can only break the program when a DOS
** I/O function is called (if the user presses break before an I/O function,
** DOS will remember the break but let the program keep running until the
** program calls an IO function). It is undesirable to have break on
** all the time since for instance if the user breaks the program after I
** wrote one sector to disk (I need to write three sectors per sub-directory
** moved), he'll end up with an inconsistent directory. The solution is to
** make sure break is off when I'm doing disk writes, and restore it
** to its orginal status when I'm done writing the disk.
** If the user really wants to mess up his disk, he can still do it with
** ctrl-alt-del or switching the power off in the middle of running mv.
*/
main(argc,argv)
int argc;
char *argv[];
{
unsigned int status, dtype, stype, status2, smask, dmask, i, drv, plimit,
doit;
union dtbuf mydta1, mydta2, tmpdta;
char fsource[PLEN], fdest[PLEN], *source, *dest, *tmp, fdest2[PLEN];
if (argc < 3) { /* print help */
putn(
"Usage: MV <source1> <source2> .. <sourceN> <dest>\n\15",
" MV renames/moves the source files/directories to the destination.\n\15",
" Wildcards ok. Specify source type with /d, /f, /h, and /i.\n\15",
" /d=sub-directories, /f=files, /h=search hidden, /i=interactive.\n\15",
" \"*.*\\.\" or \"*.*/d\" specifies all sub-directories only\n\15",
" \"*.*/f\" specifies all visible files only\n\15",
" \"*.*/hf\" specifies all visible and hidden files\n\15",
" \"*.*/fi\" will prompt you (move or not) for every file found\n\15",
" Version 1.20 made ", date, ". For DOS 2.xx and 3.xx.\n\n\15",
"Please send comments/bug reports to one of the following addresses:\n\15",
" Arpanet: pwu@unix.macc.wisc.edu\n\15",
" Bitnet: WU at WISVMACC\n\15",
" CompuServe: 76377,1332\n\15",
" UUCP: {akgua|ihnp4|seismo|harvard|allegra|ucbvax}!uwvax!uwmacc!pwu\n\15",
NULL);
exit(0);
}
/* test DOS version */
switch (_osmajor) {
case 2: /* dos 2.xx */
break;
case 3: /* dos 3.xx */
if (_osminor <= 20) { /* make sure it's no later than version 3.20 */
break;
}
default:
cputs("need DOS between version 2.00 and 3.20\n\15");
exit(1);
}
plimit = PLEN - 80; /* limit on user supplied path name */
/* process destination first */
dest = argv[argc-1];
if (strlen(dest) > plimit) {
cputs("destination path too long\n\15");
exit(1);
}
strcpy(fdest,dest);
dtype = normal(fdest); /* normalize destination path */
if (dtype == 0) { /* no specified type for destination */
dmask = A_FIL | A_DIR;
} else {
dmask = dtype;
}
status2 = ffmf(fdest, A_MASK, &mydta2); /* find info on dest */
#ifdef debug
printf("fdest is %s, dmask=%d, status2 = %d\n", fdest, dmask, status2);
#endif
if (!status2) { /* if destination exists */
#ifdef debug
printf("destination exists and has attr: %x\n", mydta2.dos.attr);
#endif
/* check if destination is ambigious here */
if (lastc(fdest) != '\\') { /* root would cause error, so don't check it */
tmpdta = mydta2; /* don't disturb mydta2, we need it later */
status = fnmf(&tmpdta);
if (!status) { /* ha, there's more than one destination! */
cputs("destination is ambigious\n\15");
exit(1);
}
}
/* if destination is an existing file, report error */
if ((mydta2.dos.attr & A_DIR) == 0) {
cputs("destination is an existing file!\n\15");
exit(1);
}
/* fix fdest so that a destination of "*\." will have the expanded name
** by removing the * and appending the directory name found by ffmf.
** This will cause an error if fdest is the root of a 'subst' disk,
** so we must make sure fdest is not a root.
*/
if (lastc(fdest) != '\\') { /* if not root then */
tmp = strrchr(fdest,'\\'); /* find last '\' */
if (tmp == NULL) { /* no '\' ????!!!! */
cputs("error after strrchr: cannot find \\\n\15");
error("front",0);
}
strcpy(tmp+1, mydta2.dos.fn); /* dest with wild cards expanded */
}
} else { /* destination not found */
#ifdef debug
printf("destination not exists\n");
#endif
if (dtype == A_DIR) { /* specified directory not exist */
cputs("can't find destination directory\n\15");
exit(1);
} else { /* destination type not specified or of file type */
/* check: source better be unambigious */
if (argc > 3) {
cputs("can't rename more than one source\n\15");
exit(1);
}
/* destination is not found, let's lookup destination's parent.
** This has to exist (e.g. if C:\X\Y is destination and doesn't
** exist, C:\X should still exists). This lookup also allows us
** to compare the drive number between the destination and the
** source to detect cross device move.
*/
strcpy(fdest2, fdest);
chopath(fdest2); /* remove last portion of path */
status = ffmf(fdest2, A_MASK, &mydta2);
if (status) { /* if destination's parent doesn't exist */
putn("path \"", fdest2, "\" not exist!\n\15", 0);
exit(1);
} else if ((mydta2.dos.attr & A_DIR) == 0) { /* not a directory */
putn("\"", fdest2, "\" is not a directory!\n\15", 0);
exit(1);
}
}
}
brk_st = bstat(); /* get current break status (on or off) */
/***********************************************************/
/* now process SOURCE **************************************/
/***********************************************************/
for (i=1; i < argc - 1; i++) { /* for all source specification */
source = argv[i];
if (strlen(source) > plimit) { /* rare user error but just in case */
cputs("source path too long! skipped\n\15");
continue; /* next argument */
}
strcpy(fsource,source);
stype = extype(fsource); /* extract type */
stype |= normal(fsource); /* normalize source path */
#ifdef debug
printf("normalized source path: %s\n", fsource);
#endif
if ((stype & (A_DIR | A_FIL)) == 0) { /* no specified type, use default */
smask = stype | A_FIL | A_DIR; /* look for file or directory */
} else {
smask = stype;
}
status = ffmf(fsource, smask, &mydta1); /* find info on source */
if (status) {
putn(source, " not found\n\15", 0);
continue;
}
/* see if source is on a network disk */
if ((mydta1.dos.drv_no < 0) || (mydta1.dos.drv_no >= LASTDRV)) {
cputs("sorry, mv doesn't work on network disks\n\15");
exit(1);
}
/* is source and destination on same drive? */
if (mydta1.dos.drv_no != mydta2.dos.drv_no) {
cputs("source and destination must be on same physical drive\n\15");
exit(1);
}
/* check: if destination does not exist, source better be unique */
if (status2) { /* destination does not exist */
if (lastc(fsource) != '\\') { /* don't call fnmf on root */
tmpdta = mydta1; /* do not change mydta1, mess with a copy */
status = fnmf(&tmpdta); /* see if there's more than one source */
if (!status) { /* source is ambigious */
cputs("can't rename more than one source\n\15");
exit(1);
}
}
}
do { /* repeat for all source wildcard */
#ifdef debug
printf("source found, fn: %s\n", mydta1.dos.fn);
printf("found attribute is %2xh\n", mydta1.dos.attr);
#endif
/* form source name with wild cards expanded */
chopath(fsource); /* chop off wild cards */
catpath(fsource, mydta1.dos.fn); /* append expanded name */
if (status2) { /* destination not exist */
if (stype & A_INT) { /* interactive mode */
doit = prompt(fsource,fdest);
} else { /* in batch mode, always do it */
putn(fsource, " ── ", fdest, " ", 0);
doit = 1; /* do it! */
}
if (doit) {
if (mydta1.dos.attr & A_DIR) { /* rename or move directory */
status = mvdir(mydta1, fsource, fdest);
if (!status) {
cputs("OK.\n\15");
}
} else { /* rename or move file */
/* don't know what rename does when user presses break key,
** so set break off to be safe
*/
bset(0); /* turn break off (disable break key) */
status = rename(fdest,fsource); /* use fdest or dest? */
bset(brk_st); /* restore break status to orginal value */
if (status) {
putn("can't\n\15",
"invalid file name/disk write protected\n\15", 0);
} else {
cputs("ok\n\15");
}
/* fall thru to fnmf */
}
} else { /* doit = 0, because the user press 'n' */
cputs("not moved\15\n");
}
} else { /* now handle the case when destination do exists */
#ifdef debug
printf("dest exists, let's see...\n");
#endif
if (mydta2.dos.attr & A_DIR) { /* destination is a directory */
#ifdef debug
printf("before strcat fdest is %s\n", fdest);
printf("now cat with %13.13s\n", mydta1.dos.fn);
#endif
/* create destination + tail of source in fdest2
** e.g. source = \user\peter\cat
** destination = \junk\haha
** fdest2 = \junk\haha\cat
*/
strcpy(fdest2, fdest); /* make a work copy */
catpath(fdest2, mydta1.dos.fn);
if (stype & A_INT) { /* interactive mode */
doit = prompt(fsource,fdest2);
} else { /* in batch mode, always do it */
putn(fsource, " ── ", fdest2, " ", 0);
doit = 1; /* do it! */
}
if (doit) {
#ifdef debug
printf("dest+tail of source: %s\n", fdest2);
#endif
if (mydta1.dos.attr & A_DIR) { /* source is also a directory */
status = mvdir(mydta1, fsource, fdest2);
if (!status) {
cputs("OK.\n\15");
}
/* fall down to fnmf */
} else { /* move a file into a directory */
bset(0); /* turn break off (disable break key) */
status = rename(fdest2, fsource);
bset(brk_st); /* set break status to orginal value */
if (status) {
cputs("can't; file exists already/disk write-protected\n\15");
} else {
cputs("ok.\n\15");
}
}
} else { /* doit = 0 */
cputs("not moved\15\n");
}
} else { /* dest is an existing file! */
putn(fsource, " ── ", fdest,
" can't rename: file exists already\n\15", 0);
}
} /* if dest exist or not */
status = fnmf(&mydta1); /* find info on next matching source */
} while (status == 0); /* while there are matching files */
} /* for different sources */
exit(0); /* no error */
}
prompt(s,d) /* return 1 for 'y'; 0 for 'n' */
char *s, *d; /* part of the prompt */
{
char c;
do {
putn(s, " ── ", d, " (y/n/q; ?=help): ", 0);
c = getkey("yYnNqQ?");
putch(' ');
switch (c) {
case '?':
putn("\15\n",
"y - yes, move it\15\n",
"n - no, skip it\15\n",
"q - quit to DOS\15\n",
"? - this help message\15\n\n",
0);
break;
case 'y':
case 'Y':
return 1;
case 'n':
case 'N':
return 0;
case 'q':
case 'Q':
cputs("Quit.\15\n");
exit(0);
default:
cputs("\15\nHow do I get here?\15\n");
error("prompt",0);
} /* end switch */
} while (1);
}
char getkey(valid) /* wait for valid key and echo it */
char *valid;
{
char c;
int i;
do {
c = getch();
i = index(valid,c);
if (i > -1) {
putch(c); /* echo valid key */
return c;
} else { /* beep at invalid key */
putch(7);
}
} while (1);
}